#include "Assembly.h"
#include "Cuboid.h"
#include "Cylinder.h"
#include "Sphere.h"
#include "WaveObj.h"
#include "MaterialLib.h"
#include "Matrix4D.h"

#include <GL/glut.h>

#include <iostream>
#include <stdexcept>
#include <sstream>

using std::runtime_error;
using std::cerr;
using std::cout;
using std::endl;
using std::stringstream;

Assembly *scene;
Assembly *cuboid1, *cuboid2, *cuboid3;
Assembly *cylinder1, *cylinder2;
Assembly *sphere1, *sphere2;
Assembly *wave1, *wave2;

float rot = 0.0f;

void setMatrix()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0f, 1.0f, 1.0f, 10.0f);
	
	glMatrixMode(GL_MODELVIEW);
	gluLookAt(
	    0.0f, 0.0f, 5.0f,
	    0.0f, 0.0f, 0.0f,
	    0.0f, 1.0f, 0.0f
	);
}

void setLights()
{
	// alpha = 1.0	=>	not transparent
	float lamb[] = {0.1f, 0.1f, 0.1f, 0.0f};
	float ldiff[] = {0.7f, 0.7f, 0.7f, 1.0f};
	float lspec[] = {1.0f, 1.0f, 1.0f, 0.0f};
	// position, (last value > 0) => positional, directional otherwise
	float lpos[] = {0.0f, 0.0f, 1.0f, 0.0f};
	
	float mamb[] = {0.8f, 0.8f, 0.0f, 1.0f};
	float mdiff[] = {0.8f, 0.8f, 0.0f, 1.0f};
	float mspec[] = {0.2f, 0.2f, 0.2f, 1.0f};
	float memiss[] = {0.0f, 0.0f, 0.0f, 1.0f};
	float mshine[] = {120.0};
	
	glEnable(GL_LIGHTING);
	
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_AMBIENT, lamb);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, ldiff);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lspec);
	glLightfv(GL_LIGHT0, GL_POSITION, lpos);
	
	glMaterialfv(GL_FRONT, GL_AMBIENT, mamb);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mdiff);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mspec);
	glMaterialfv(GL_FRONT, GL_EMISSION, memiss);
	glMaterialfv(GL_FRONT, GL_SHININESS, mshine);
}

void initGL()
{
	glClearColor(0.3f, 0.3f, 0.1f, 1.0f);
	setMatrix();
	setLights();
	
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_CULL_FACE);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_NORMALIZE);
}

void init(int& argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(300, 300);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("clean Assembly");
	
	initGL();
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	scene->rotation().setY(rot);
	scene->rotation().setZ(rot/2.0f);
	scene->draw();
	
	glutSwapBuffers();
	rot += 5.0f;
	if (rot > 360.0f)
	{
		rot -= 360.0f;
	}
}

int main(int argc, char **argv)
{
	try
	{
#ifndef VALGRIND
		init(argc, argv);
#endif
		
		scene = new Assembly();
		scene->name() = "scene";
		
		ifstream ifs(argv[1]);
		cout << "start parsing obj" << endl;
		wave1 = new WaveObj(ifs);
		cout << "stop parsing obj" << endl;
		ifs.close();
		
		scene->addPart(wave1);
		
//		cuboid1 = new Cuboid(1.0f, 1.0f, 1.0f);
//		cuboid1->name() += "1";
//		cuboid1->material() = "red";
//		scene->addPart(cuboid1);
//
//		cuboid2 = new Cuboid(1.0f, 1.0f, 1.0f, -2.0f, 0.0f, 0.0f);
//		cuboid2->name() += "2";
//		cuboid2->material() = "green";
//		cuboid2->rotation().setZ(45.0f);
//		scene->addPart(cuboid2);
//
//		cuboid3 = new Cuboid(0.5f, 1.0f, 0.5f, 0.0f, 1.0f, 0.0f);
//		cuboid3->name() += "3";
//		cuboid3->material() = "yellow";
//		scene->addPart(cuboid3);
//
//		sphere1 = new Sphere(0.6f, 0.0f, 0.5f, 0.0f);
//		sphere1->rotation().setY(15.0f);
//		sphere1->name() += "1";
//		sphere1->material() = "pink";
//		scene->addPart(sphere1);
//
//		sphere2 = new Sphere(0.3f, 0.0f, 0.0f, 0.6f);
//		sphere2->name() += "2";
//		sphere2->material() = "white";
//		sphere1->addPart(sphere2);
//
//		cylinder1 = new Cylinder(0.3f, 0.5f, 0.5f,
//		                         0.5f, 0.0f, 0.75f,
//		                         90.0f, 0.0f, 0.0f);
//		cylinder1->name() += "1";
//		cylinder1->material() = "sapphire";
//		cuboid2->addPart(cylinder1);
//
//		cylinder2 = new Cylinder(0.3f, 0.5f, 0.5f,
//		                         1.0f, 0.0f, 0.0f,
//		                         0.0f, 0.0f, 0.0f);
//		cylinder2->name() += "2";
//		cylinder2->material() = "orange";
//		cylinder1->addPart(cylinder2);
//
		stringstream ss, ms;
		scene->exportObj(ss, ms);
		while (ss)
		{
			string s;
			getline(ss, s);
			cout << s << endl;
		}
		
		while (ms)
		{
			string s;
			getline(ms, s);
			cout << s << endl;
		}
		
#ifndef VALGRIND
		glutDisplayFunc(display);
		glutMainLoop();
#endif
		delete scene;
	}
	catch (runtime_error& err)
	{
		cerr << err.what() << endl;
	}
	
	MaterialLib::getInstance()->destroy();
	
	return 0;
}

